#ifndef K_MUTEX_H
#define K_MUTEX_H
#pragma once

#include "KEvent.h"

class KLock : KNoCopy {
public:
    KLock()         { InitializeCriticalSection(&m_cs); }
    ~KLock()        { DeleteCriticalSection(&m_cs); }
public:
    void lock()     { EnterCriticalSection(&m_cs); }
    bool tryLock()  { return !!TryEnterCriticalSection(&m_cs); }
    void unlock()   { LeaveCriticalSection(&m_cs); }
private:
    CRITICAL_SECTION m_cs;
};

class KRWLock : KNoCopy {
public:
    KRWLock();
public:
    void lockRead();
    void lockWrite();
    void unlock();
private:
    enum State {
        Idle,
        Reading,
        Writing
    };
private:
    KLock           m_entry;
    KManualEvent    m_event;
    size_t          m_nRead;
    volatile size_t m_nWaitingRead;
    volatile size_t m_nWaitingWrite;
    volatile State  m_state;
};

class KMutex : KNoCopy {
public:
    explicit KMutex(KLock &lock) : m_lock(lock)         { m_lock.lock(); }
    ~KMutex()                                           { m_lock.unlock(); }
private:
    KLock &m_lock;
};

template <class ClassT>
class KMutexFor : KNoCopy {
public:
    KMutexFor()                                         { s_lock.lock(); }
    ~KMutexFor()                                        { s_lock.unlock(); }
private:
    // One instance for each ClassT
    static KLock s_lock;
};

template <class ClassT>
KLock KMutexFor<ClassT>::s_lock;

class KMutexWrite : KNoCopy {
public:
    explicit KMutexWrite(KRWLock &lock) : m_lock(lock)  { m_lock.lockWrite(); }
    ~KMutexWrite()                                      { m_lock.unlock(); }
private:
    KRWLock &m_lock;
};

class KShare : KNoCopy {
public:
    explicit KShare(KLock &lock) : m_lock(lock)         { m_lock.unlock(); }
    ~KShare()                                           { m_lock.lock(); }
private:
    KLock &m_lock;
};

class KShareRead : KNoCopy {
public:
    explicit KShareRead(KRWLock &lock) : m_lock(lock)   { m_lock.lockRead(); }
    ~KShareRead()                                       { m_lock.unlock(); }
private:
    KRWLock &m_lock;
};

#endif // K_MUTEX_H